/*
STC89C52RC内部EEPROM详细地址表：
第一扇区                   第二扇区                    第三扇区                    第四扇区
起始地址  结束地址      起始地址   结束地址         起始地址   结束地址         起始地址    结束地址
2000h      21FFh       2200h       23FFh         2400h       25FFh          2600h        27FFH
第五扇区                    第六扇区                    第七扇区                    第八扇区
起始地址   结束地址     起始地址   结束地址         起始地址   结束地址         起始地址    结束地址
2800h       29FFh      2A00h      2BFFh          2C00h      2DFFh           2E00h        2FFFh
*/
#include <REG52.H>
#include <INTRINS.H>
#define uchar unsigned char
#define uint unsigned int
/*Declare SFR associated with the IAP 特殊功能寄存器声明*/
sfr IAP_DATA = 0xE2;  //Flash data register
sfr IAP_ADDRH = 0xE3; //Flash address HIGH
sfr IAP_ADDRL = 0xE4; //Flash address LOW
sfr IAP_CMD = 0xE5;   //Flash command register
sfr IAP_TRIG = 0xE6;  //Flash command trigger
sfr IAP_CONTR = 0xE7; //Flash control register
/*Define ISP/IAP/EEPROM command*/
#define CMD_IDLE 0    //Stand-By
#define CMD_READ 1    //Byte-Read 读
#define CMD_PROGRAM 2 //Byte-Program 写
#define CMD_ERASE 3   //Sector-Erase 擦
/*Define ISP/IAP/EEPROM operation const for IAP_CONTR*/
// #define ENABLE_IAP 0x80 //if SYSCLK<40MHz
#define ENABLE_IAP 0x81 //if SYSCLK<20MHz stc89C51系列11.0592MHz
// #define ENABLE_IAP x82  //if SYSCLK<10MHz
// #define ENABLE_IAP 0x83 //if SYSCLK<5MHz

#define IAP_ADDRESS 0x2000
uchar busy = 1;

// void UartSendStr(uchar *str);
// eeprom
void DelayMS(uchar ms);
void IapIdle();
uchar IapReadByte(uint addr);
void IapWriteByte(uint addr, uchar dat);
void IapWriteStr(uint addr, uchar *str);
void IapEraseSector(uint addr);
// char IapReadStr(uint addr);
uchar CheckFF();
/**************************************************
函数: 延时函数()
描述:
    毫秒
/**************************************************/
void DelayMS(uchar ms)
{
    uchar i, j;
    i = 0;
    for (i = 0; i < ms; i++)
    {
        j = 250;
        while (--j)
            ;
        j = 250;
        while (--j)
            ;
    }
}

/*----------------------------
Disable ISP/IAP/EEPROM function
Make MCU in a safe state
函数：关闭ISP/IAP操作
----------------------------*/
void IapIdle()
{
    IAP_CONTR = 0; //Close IAP function// 关闭IAP功能
    IAP_CMD = 0;   //Clear command to standby// 待机模式，无ISP操作
    IAP_TRIG = 0;  //Clear trigger register// 关闭IAP功能, 清与ISP有关的特殊功能寄存器
    // IAP_ADDRH = 0x80; //Data ptr point to non-EEPROM area
    // IAP_ADDRL = 0;    //Clear IAP address to prevent misuse
}
/*----------------------------
Read one byte from ISP/IAP/EEPROM area
Input: addr (ISP/IAP/EEPROM address)
Output:Flash data
函数：读一字节
入口：addr = 扇区单元地址
出口：dat  = 读出的数据
----------------------------*/
uchar IapReadByte(uint addr)
{
    uchar dat; //Data buffer

    IAP_CONTR = ENABLE_IAP; //Open IAP function, and set wait time
    IAP_CMD = CMD_READ;     //Set ISP/IAP/EEPROM READ command // 用户可以对"Data Flash/EEPROM区"进行字节读
    IAP_ADDRL = addr;       //Set ISP/IAP/EEPROM address low
    IAP_ADDRH = addr >> 8;  //Set ISP/IAP/EEPROM address high
    IAP_TRIG = 0x46;        //Send trigger command1 (0x46)
    IAP_TRIG = 0xb9;        //Send trigger command2 (0xb9)
    _nop_();                //MCU will hold here until ISP/IAP/EEPROM operation complete
    dat = IAP_DATA;         //Read ISP/IAP/EEPROM data // 取出数据
    IapIdle();              //Close ISP/IAP/EEPROM function
    return dat;             //Return Flash data
}
/*----------------------------
Program one byte to ISP/IAP/EEPROM area
Input: addr (ISP/IAP/EEPROM address)
       dat (ISP/IAP/EEPROM data)
Output:-
函数：写一字节
入口：addr = 扇区单元地址 , dat = 待写入数据
----------------------------*/
void IapWriteByte(uint addr, uchar dat)
{
    IAP_CONTR = ENABLE_IAP; //Open IAP function, and set wait time
    IAP_CMD = CMD_PROGRAM;  //Set ISP/IAP/EEPROM PROGRAM command // 用户可以对"Data Flash/EEPROM区"进行字节编程
    IAP_ADDRL = addr;       //Set ISP/IAP/EEPROM address low
    IAP_ADDRH = addr >> 8;  //Set ISP/IAP/EEPROM address high
    IAP_DATA = dat;         //Write ISP/IAP/EEPROM data
    IAP_TRIG = 0x46;        //Send trigger command1 (0x46)
    IAP_TRIG = 0xb9;        //Send trigger command2 (0xb9)
    _nop_();                //MCU will hold here until ISP/IAP/EEPROM operation complete
    IapIdle();
}
/*----------------------------
Erase one sector area
Input: addr (ISP/IAP/EEPROM address)
Output:-
函数：擦除某一扇区（每个扇区512字节）
入口：addr = 某一扇区首地址  
----------------------------*/
void IapEraseSector(uint addr)
{
    IAP_CONTR = ENABLE_IAP; //Open IAP function, and set wait time
    IAP_CMD = CMD_ERASE;    //Set ISP/IAP/EEPROM ERASE command // 用户可以对"Data Flash/EEPROM区"进行扇区擦除
    IAP_ADDRL = addr;       //Set ISP/IAP/EEPROM address low
    IAP_ADDRH = addr >> 8;  //Set ISP/IAP/EEPROM address high
    IAP_TRIG = 0x46;        //Send trigger command1 (0x46)
    IAP_TRIG = 0xb9;        //Send trigger command2 (0xb9)
    _nop_();                //MCU will hold here until ISP/IAP/EEPROM operation complete
    IapIdle();
}
/*----------------------------
函数：判断是否擦除干净
完全擦除返回1;否则返回0 
----------------------------*/
uchar CheckFF()
{
    uchar i;
    for (i = 0; i < 255; i++)
    {
        if (IapReadByte(IAP_ADDRESS + i) != 0xff)
            return 0;
    }
    return 1;
}